package com.tutu.borrow.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.fastjson.JSONObject;
import com.tutu.borrow.entity.Borrow;
import com.tutu.borrow.entity.Dog;
import com.tutu.borrow.service.BorrowService;
import com.tutu.commons.entity.Book;
import com.tutu.commons.entity.User;
import com.tutu.commons.feign.BookClient;
import com.tutu.commons.feign.UserClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @Program: SpringCloud
 * @Description: BorrowController
 * @Author: JK.TuTu
 * @ @Version: 1.0
 * @Create: 2022-12-01 14:39
 **/
@Slf4j
@RestController
@RequestMapping("/borrow")
@RefreshScope   //添加该注解实现远程配置文件自动刷新
public class BorrowController {
    @Resource
    private BorrowService borrowService;


    @Resource
    private UserClient userClient;

    @Resource
    private BookClient bookClient;

    @Resource
    private Dog dog;



    /**
     * 通过id查询出借阅信息
     */
    @GetMapping("/getBorrowInfo/{id}")
    public Borrow getBorrow(@PathVariable("id") int id) {
        //通过id查询图书的借阅信息
        Borrow borrowInfo = borrowService.getBorrowInfo(id);

        //采用RestTemplate发送请求  通过用户id查询出用户信息
        User user = userClient.findUserById(borrowInfo.getUid());
        //通过图书id查询图书信息
        Book book = bookClient.findBookById(borrowInfo.getBid());
        borrowInfo.setUser(user);
        borrowInfo.setBook(book);
        return borrowInfo;
    }


    /**
     * 测试远程配置文件属性注入
     */
    /*
     * @SentinelResource注解: 监控此方法 类似于@HystrixCommand
     * value 自定义 表示资源的名称
     * blockHandler 指定当该方法被限流后的替代解决方法 这样就不会使用默认的抛出异常的形式 blockHandler只能处理限流情况下抛出的异常
     * fallback 指定出现其他异常时的替代解决方法 此种处理方案会在没有配置blockHandler的情况下一并处理限流控制下的异常
     * exceptionsToIgnore 忽略哪些异常 即即时出现这些异常也不会使用替代方法
     *
     */
    @SentinelResource(value = "dog",
            blockHandler = "blockErrorMethod",
            fallback = "otherErrorMethod",
            exceptionsToIgnore = IOException.class)
    @GetMapping("dog")
    public Dog getDog() {
        return dog;
    }

    /**
     * blockHandler处理的替代方法 要求参数和返回值需要保持一致 并且参数最后还需要添加一个BlockException
     */
    public Dog blockErrorMethod(BlockException e) {
        log.info("blockHandler处理的解决方案........");
        return new Dog();
    }

    /**
     * fallback 处理其他异常的解决方法 要求该方法必须与原方法参数和方法返回值保持一致 最后可以添加一个Throwable作为参数接受异常
     */
    public Dog otherErrorMethod(Throwable throwable) {
        log.info("fallback处理的解决方法........");
        return new Dog();
    }

    /**
     * 测试热点参数限流
     */
    @PostMapping("/paramTest")
    @SentinelResource(value = "paramTest", blockHandler = "paramErrorMethod")
    public String paramTest(@RequestParam(value = "a", required = false) Integer a,
                            @RequestParam(value = "b", required = false) Integer b,
                            @RequestParam(value = "c", required = false) Integer c) {
        return "请求成功！a=" + a + ",b=" + b + ",c=" + c;
    }

    /**
     * 热点参数限流后的备用方法
     */
    public String paramErrorMethod(Integer a, Integer b, Integer c, BlockException e) {
        log.info("热点参数限流后抛出的异常:{}", e.getClass());
        return "热点参数限流后的备用方法......";
    }

    /**
     * 测试服务熔断和服务降级的方法
     */
    @GetMapping("/test")
    @SentinelResource(value = "test", blockHandler = "serverErrorMethod")
    public String test() {
      /*  try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }*/
        int a = 1 / 0;
        return "测试服务熔断和服务降级的方法.......";
    }

    /**
     * 服务熔断后的备用方案
     */
    public String serverErrorMethod(BlockException e) {
        log.info("服务熔断后抛出的异常:{}", e.getClass()); //DegradeException 会抛出服务降级的异常
        return "服务熔断后的备用方案........";
    }

    /**
     * 全局自定义流量限制时的返回方法  需要在yml配置文件中配置该方法的资源访问路径
     */
    @RequestMapping("/blocked")
    JSONObject blocked() {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("code", 403);
        jsonObject.put("message", "您的请求频率过快,请稍后再试!");
        return jsonObject;
    }


    /**
     * 借阅图书接口
     */
    @GetMapping("doBorrow/{uid}/{bid}")
    public Map<String, Object> doBorrow(@PathVariable("uid") int uid,
                                       @PathVariable("bid") int bid){
        borrowService.doBorrow(uid,bid);
        Map<String, Object> map = new HashMap<>();
        map.put("code",200);
        map.put("message","图书借阅成功");
        return map;
    }
}
